package com.young.testcoroutine

import kotlinx.coroutines.*
import org.junit.Test
import java.lang.IndexOutOfBoundsException

/**
 * Author：young
 * .
 * Time： 2022/9/14 0:33 周三
 * .
 * Desc：协程的异常处理
 */
class CoroutineTest07 {

    /**
     * 协程上下文组合
     */
    @Test
    fun `test CoroutineContext`() = runBlocking<Unit> {
        launch(Dispatchers.Default + CoroutineName("young_test_coroutine")) {
            println("run on thread ${Thread.currentThread().name}")
        }
    }

    /**
     * 协程上下文的继承
     */
    @Test
    fun `test CoroutineContext extend`() = runBlocking<Unit> {
        val scope = CoroutineScope(Dispatchers.IO + CoroutineName("young_test_coroutine"))

        val job = scope.launch {
            println("${coroutineContext[Job]} ${Thread.currentThread().name}")
            val result = async {
                println("${coroutineContext[Job]} ${Thread.currentThread().name}")
                "返回结果"
            }.await()
        }

        job.join()

    }

    /**
     * 协程上下文拼接（理解上下文继承）
     */
    @Test
    fun `test CoroutineContext extend2`() = runBlocking<Unit> {
//        val coroutineExceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
//            println("caught ${throwable.message}")
//        }
        // 不需要使用的参数，可以用 _ 代替，可以节省一点内存
        val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
            println("caught ${throwable.message}")
        }

        val scope = CoroutineScope(Job() + Dispatchers.Main + coroutineExceptionHandler)

        val job = scope.launch(Dispatchers.IO) {
            // 新的协程

        }
    }

    /**
     * 异常传播
     */
    @Test
    fun `test Exception Propagation`() = runBlocking<Unit> {
        val job = GlobalScope.launch {
            // 异常发生在这个位置
            throw IndexOutOfBoundsException()
        }
        job.join()

        val deferred = GlobalScope.async {
            throw ArithmeticException()
        }
        // 异常发生在这个位置
        deferred.await()
    }

    /**
     * 异常传播，catch 异常
     */
    @Test
    fun `test Exception Propagation1`() = runBlocking<Unit> {
        val job = GlobalScope.launch {
            // 异常发生在这个位置
            try {
                throw IndexOutOfBoundsException()
            } catch (e: Exception) {
                println("catch IndexOutOfBoundsException ${e.message}")
            }
        }
        job.join()

        val deferred = GlobalScope.async {
            throw ArithmeticException()
        }
        // 异常发生在这个位置
        // 依赖用户来最终消费异常,例如通过await或receive
        // 如果将  deferred.await() 注释，就不会有 ArithmeticException
        try {
            deferred.await()
        } catch (e: Exception) {
            println("catch ArithmeticException ${e.message}")
        }
    }


    /**
     * 非根协程的异常传播
     *
     * 非根协程，不需要调用 await 等，有异常直接就抛出
     *
     */
    @Test
    fun `test exception propagation2`() = runBlocking {
        val scope = CoroutineScope(Job())
        val job = scope.launch {
            async {
                // 如果async抛出异常，launch就会立即抛出异常，而不会调用.await ()
                throw IllegalArgumentException()
            }
        }
        // 这里是把新协程加入到当前协程中进行等待
        job.join()
    }

    /**
     * 测试 SupervisorJob
     */
    @Test
    fun `test SupervisorJob`() = runBlocking<Unit> {
        val scope = CoroutineScope(SupervisorJob())

        val job = scope.launch {
            delay(100)
            println("协程 1")
            throw IllegalArgumentException()
        }

        val job2 = scope.launch {
            try {
                while (isActive) {
                    delay(1000)
                    println("协程 2 ${System.currentTimeMillis()}")
                }
            } finally {
                println("协程2 finally")
            }
        }

        joinAll(job, job2)

    }

    /**
     * 使用 job 执行，发现其他子协程也关闭了
     */
    @Test
    fun `test super`() = runBlocking<Unit> {
        val scope = CoroutineScope(Job())

        val job = scope.launch {
            delay(100)
            println("协程 1")
            throw IllegalArgumentException()
        }

        val job2 = scope.launch {
            try {
                while (isActive) {
                    delay(1000)
                    println("协程 2 ${System.currentTimeMillis()}")
                }
            } finally {
                println("协程2 finally")
            }
        }

        joinAll(job, job2)

    }

    /**
     * 测试 SupervisorJob 取消，取消了，全部子协程和本身都取消了
     */
    @Test
    fun `test SupervisorJob Cancel`() = runBlocking<Unit> {
        val scope = CoroutineScope(SupervisorJob())

        val job = scope.launch {
            delay(100)
            println("协程 1")
            throw IllegalArgumentException()
        }

        val job2 = scope.launch {
            try {
                while (isActive) {
                    delay(1000)
                    println("协程 2 ${System.currentTimeMillis()}")
                }
            } finally {
                println("协程2 finally")
            }
        }

        delay(200)
        scope.cancel()

        joinAll(job, job2)

    }


    /**
     * 测试 supervisorScope 的使用，效果跟 SupervisorJob 一样
     */
    @Test
    fun `test supervisorScope`() = runBlocking<Unit> {
        supervisorScope {
            launch {
                delay(100)
                println("协程 1")
                throw IllegalArgumentException()
            }


            try {
                while (true) {
                    delay(1000)
                    println("协程2 ${System.currentTimeMillis()}")
                }
            } finally {
                println("supervisorScope 被干掉了")
            }
        }
    }


    /**
     * 测试 supervisorScope 的使用，让自己抛出异常，子协程也会被取消
     */
    @Test
    fun `test supervisorScope2`() = runBlocking<Unit> {
        supervisorScope {
            val child = launch {
                try {
                    delay(Long.MAX_VALUE)
                    println("协程2 ${System.currentTimeMillis()}")
                } finally {
                    println("child 被干掉了")
                }
            }

            yield()
            println("supervisorScope 准备抛出异常")
            throw AssertionError()

        }
    }

}